package com.ard.camera.demos.service;

import com.ard.camera.demos.DvrHostPingChecker;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

@Component
@Slf4j
public class DvrMonitorService {

    @Autowired
    private DvrHostPingChecker dvrHostPingChecker;

    @Autowired
    private DvrEmailService dvrEmailService;

    // 记录上次检查失败的结果（按硬盘录像机IP和主机地址）
    private final Map<String, Set<String>> lastFailedHosts = new HashMap<>();

    /**
     * 定时硬盘录像机主机监控任务
     */
    @Scheduled(cron = "0 0 9,15 * * ?")
    public void monitorDvrHosts() {
        log.info("开始执行硬盘录像机主机监控检查...");

        Map<String, List<DvrHostPingChecker.DvrHostCheckResult>> allResults =
                dvrHostPingChecker.checkAllDvrHosts();

        // 提取失败的主机，按硬盘录像机分组
        Map<String, List<DvrHostPingChecker.DvrHostCheckResult>> failedDvrHosts = new HashMap<>();

        for (Map.Entry<String, List<DvrHostPingChecker.DvrHostCheckResult>> entry : allResults.entrySet()) {
            String dvrIp = entry.getKey();
            List<DvrHostPingChecker.DvrHostCheckResult> failedHosts = entry.getValue().stream()
                    .filter(result -> !result.isReachable())
                    .collect(Collectors.toList());

            if (!failedHosts.isEmpty()) {
                failedDvrHosts.put(dvrIp, failedHosts);
            }
        }

        // 检查是否有新的失败主机（按硬盘录像机和主机地址判断）
        boolean hasNewFailures = checkForNewFailures(failedDvrHosts);

        // 只有当有新的失败主机时才发送邮件
        if (hasNewFailures && !failedDvrHosts.isEmpty()) {
            dvrEmailService.sendDvrHostAlert(failedDvrHosts);

            // 更新记录
            updateLastFailedHosts(failedDvrHosts);

            // 记录日志
            for (Map.Entry<String, List<DvrHostPingChecker.DvrHostCheckResult>> entry : failedDvrHosts.entrySet()) {
                String dvrIp = entry.getKey();
                List<String> failedDisplayNames = entry.getValue().stream()
                        .map(result -> result.getDvrHostEntry().getHostName() +
                                "(" + result.getDvrHostEntry().getHostAddress() + ")")
                        .collect(Collectors.toList());
                log.warn("硬盘录像机 {} 发现无法访问的主机: {}", dvrIp, failedDisplayNames);
            }

        } else if (failedDvrHosts.isEmpty()) {
            // 如果没有失败主机，清空记录
            if (!lastFailedHosts.isEmpty()) {
                log.info("所有硬盘录像机的主机恢复访问");
                dvrEmailService.sendSMessage("所有硬盘录像机的主机恢复访问");
                lastFailedHosts.clear();
            }
        }
    }

    /**
     * 检查是否有新的失败主机
     */
    private boolean checkForNewFailures(
            Map<String, List<DvrHostPingChecker.DvrHostCheckResult>> currentFailedDvrHosts) {

        // 如果当前失败列表为空，但之前有记录，说明有恢复
        if (currentFailedDvrHosts.isEmpty() && !lastFailedHosts.isEmpty()) {
            return true;
        }

        // 检查每个硬盘录像机
        for (Map.Entry<String, List<DvrHostPingChecker.DvrHostCheckResult>> entry :
                currentFailedDvrHosts.entrySet()) {
            String dvrIp = entry.getKey();
            List<DvrHostPingChecker.DvrHostCheckResult> currentFailedHosts = entry.getValue();

            // 获取之前记录的失败主机地址
            Set<String> lastFailed = lastFailedHosts.getOrDefault(dvrIp, new HashSet<>());

            // 获取当前失败的地址
            Set<String> currentFailed = currentFailedHosts.stream()
                    .map(result -> result.getDvrHostEntry().getHostAddress())
                    .collect(Collectors.toSet());

            // 如果当前失败集合与之前不同，说明有变化
            if (!currentFailed.equals(lastFailed)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 更新记录的失败主机
     */
    private void updateLastFailedHosts(
            Map<String, List<DvrHostPingChecker.DvrHostCheckResult>> failedDvrHosts) {

        lastFailedHosts.clear();

        for (Map.Entry<String, List<DvrHostPingChecker.DvrHostCheckResult>> entry :
                failedDvrHosts.entrySet()) {
            String dvrIp = entry.getKey();
            Set<String> failedAddresses = entry.getValue().stream()
                    .map(result -> result.getDvrHostEntry().getHostAddress())
                    .collect(Collectors.toSet());

            lastFailedHosts.put(dvrIp, failedAddresses);
        }
    }
}